package com.xwq.thumb.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xwq.thumb.constant.ThumbConstant;
import com.xwq.thumb.manager.cache.CacheManager;
import com.xwq.thumb.model.dto.DoThumbRequest;
import com.xwq.thumb.model.entity.Blog;
import com.xwq.thumb.model.entity.Thumb;
import com.xwq.thumb.model.entity.User;
import com.xwq.thumb.service.BlogService;
import com.xwq.thumb.service.ThumbService;
import com.xwq.thumb.mapper.ThumbMapper;
import com.xwq.thumb.service.UserService;
import com.xwq.thumb.utils.RedisKeyUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * @author XWQ
 * @description 针对表【thumb】的数据库操作Service实现
 * @createDate 2025-04-17 20:10:16
 *
 * 这个是第三期结束优化读，使用了冷热分离和分布式锁
 */
import java.util.Date;
import java.util.concurrent.TimeUnit;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

@Service("thumbServiceHotAndClod")
@Slf4j
@RequiredArgsConstructor
public class ThumbServiceHotAndClodImpl
        extends ServiceImpl<ThumbMapper, Thumb>
        implements ThumbService {

    private final UserService userService;

    private final BlogService blogService;

    private final TransactionTemplate transactionTemplate;

    private final RedissonClient redissonClient;

    private final RedisTemplate<String, Object> redisTemplate;

    // 引入缓存管理
    private final CacheManager cacheManager;

    private static final String LOCK_PREFIX = "thumb:lock:";

    // 热区天数
    private static final long HOT_DAYS = 30;

    @Override
    public Boolean doThumb(DoThumbRequest req, HttpServletRequest request) {
        Long blogId = req.getBlogId();
        User user = userService.getLoginUser(request);

        // 1. 读取创建时间（Date 类型）
        Date createTime = blogService.getById(blogId).getCreatetime();
        long nowMillis = System.currentTimeMillis();
        long createMillis = createTime.getTime();
        // 2. 计算是否在热区（30 天内）
        long hotWindowMillis = HOT_DAYS * 24L * 60 * 60 * 1000;
        boolean isHot = (nowMillis - createMillis) <= hotWindowMillis;

        if (isHot) {
            return doThumbHot(blogId, user.getId(), nowMillis, createMillis, hotWindowMillis);
        } else {
            return doThumbCold(blogId, user.getId());
        }
    }

    private Boolean doThumbHot(Long blogId, Long userId,
                               long nowMillis, long createMillis, long hotWindowMillis) {
        String key = RedisKeyUtil.getUserThumbKey(userId);
        String field = blogId.toString();
        RLock lock = redissonClient.getLock(LOCK_PREFIX + userId + ":" + blogId);

        try {
            if (!lock.tryLock(5, 10, TimeUnit.SECONDS)) {
                throw new RuntimeException("未能获取分布式锁");
            }
            return transactionTemplate.execute(status -> {
                if (redisTemplate.opsForHash().hasKey(key, field)) {
                    throw new RuntimeException("用户已点赞");
                }
                // 更新点赞数
                blogService.lambdaUpdate()
                        .eq(Blog::getId, blogId)
                        .setSql("thumbcount = thumbcount + 1")
                        .update();
                // 写库
                Thumb thumb = new Thumb(userId, blogId);
                this.save(thumb);

                // 计算剩余 TTL 秒数：热区窗口减去已过毫秒，转换成秒
                long remainingMillis = createMillis + hotWindowMillis - nowMillis;
                long ttlSeconds = Math.max(remainingMillis / 1000, 1);

                redisTemplate.opsForHash().put(key, field, thumb.getId());
                redisTemplate.expire(key, ttlSeconds, TimeUnit.SECONDS);
                return true;
            });
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("获取锁被中断", e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private Boolean doThumbCold(Long blogId, Long userId) {
        if (this.hasThumbInDb(blogId, userId)) {
            throw new RuntimeException("用户已点赞（冷区）");
        }
        Thumb thumb = new Thumb(userId, blogId);
        return this.save(thumb);
    }

    @Override
    public Boolean undoThumb(DoThumbRequest req, HttpServletRequest request) {
        Long blogId = req.getBlogId();
        User user = userService.getLoginUser(request);

        Date createTime = blogService.getById(blogId).getCreatetime();
        long nowMillis = System.currentTimeMillis();
        long createMillis = createTime.getTime();
        long hotWindowMillis = HOT_DAYS * 24L * 60 * 60 * 1000;
        boolean isHot = (nowMillis - createMillis) <= hotWindowMillis;

        if (isHot) {
            return undoThumbHot(blogId, user.getId());
        } else {
            return remove(new QueryWrapper<Thumb>()
                    .eq("userid", user.getId())
                    .eq("blogid", blogId)
            );
        }
    }

    private Boolean undoThumbHot(Long blogId, Long userId) {
        String key = RedisKeyUtil.getUserThumbKey(userId);
        String field = blogId.toString();
        RLock lock = redissonClient.getLock(LOCK_PREFIX + userId + ":" + blogId);

        try {
            if (!lock.tryLock(5, 10, TimeUnit.SECONDS)) {
                throw new RuntimeException("未能获取分布式锁");
            }
            return transactionTemplate.execute(status -> {
                Object thumbId = redisTemplate.opsForHash().get(key, field);
                if (thumbId == null) {
                    throw new RuntimeException("用户未点赞");
                }
                blogService.lambdaUpdate()
                        .eq(Blog::getId, blogId)
                        .setSql("thumbcount = thumbcount - 1")
                        .update();
                this.removeById(((Number) thumbId).longValue());
                redisTemplate.opsForHash().delete(key, field);
                return true;
            });
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("获取锁被中断", e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

//    @Override
//    public Boolean hasThumb(Long blogId, Long userId) {
//        Date createTime = blogService.getById(blogId).getCreatetime();
//        long nowMillis = System.currentTimeMillis();
//        boolean isHot = (nowMillis - createTime.getTime()) <= HOT_DAYS * 24L * 60 * 60 * 1000;
//        if (isHot) {
//            return redisTemplate.opsForHash()
//                    .hasKey(RedisKeyUtil.getUserThumbKey(userId), blogId.toString());
//        } else {
//            return hasThumbInDb(blogId, userId);
//        }
//    }


    @Override
    public Boolean hasThumb(Long blogId, Long userId) {
        Date createTime = blogService.getById(blogId).getCreatetime();
        long nowMillis = System.currentTimeMillis();
        boolean isHot = (nowMillis - createTime.getTime()) <= HOT_DAYS * 24L * 60 * 60 * 1000;
        if (isHot) {
            Object thumbIdObj = cacheManager.get(ThumbConstant.USER_THUMB_KEY_PREFIX + userId, blogId.toString());
            if (thumbIdObj == null) {
                return false;
            }
            Long thumbId = (Long) thumbIdObj;
            return !thumbId.equals(ThumbConstant.UN_THUMB_CONSTANT);
        } else {
            return hasThumbInDb(blogId, userId);
        }
    }


    private boolean hasThumbInDb(Long blogId, Long userId) {
        return count(new QueryWrapper<Thumb>()
                .eq("userid", userId)
                .eq("blogid", blogId)
        ) > 0;
    }
}






